package com.hl.qzzpbackend.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hl.qzzpbackend.common.DeleteRequest;
import com.hl.qzzpbackend.constant.RedisConstant;
import com.hl.qzzpbackend.constant.SessionConstant;
import com.hl.qzzpbackend.exception.BusinessException;
import com.hl.qzzpbackend.exception.ErrorCode;
import com.hl.qzzpbackend.exception.ThrowUtils;
import com.hl.qzzpbackend.model.dto.user.*;
import com.hl.qzzpbackend.model.entity.User;
import com.hl.qzzpbackend.model.enums.SendEmailCodeEnum;
import com.hl.qzzpbackend.model.enums.UserRoleEnum;
import com.hl.qzzpbackend.model.vo.UserVO;
import com.hl.qzzpbackend.service.AsyncService;
import com.hl.qzzpbackend.service.UserService;
import com.hl.qzzpbackend.mapper.UserMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 用户业务层 helong
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    final String SALT = "user";
    final String INITIAL_PASSWORD = "12345678";

    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Resource
    private UserMapper userMapper;
    @Resource
    private AsyncService asyncService;

    /**
     * 用户账号登录
     * @param userAccountLoginRequest 用户账号登录请求类
     * @param request
     * @return 用户信息
     */
    @Override
    public UserVO userAccountLogin(UserAccountLoginRequest userAccountLoginRequest, HttpServletRequest request) {
        String account = userAccountLoginRequest.getAccount();
        String password = userAccountLoginRequest.getPassword();

        // 加密密码
        String encryptPassword = this.encryptPassword(password);

        // 操作数据库查询用户
        User user = this.lambdaQuery()
                .eq(User::getAccount, account)
                .eq(User::getPassword, encryptPassword)
                .one();

        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR,"账号或密码错误");


        // 保存用户的登录态 脱敏
        request.getSession().setAttribute(SessionConstant.USER_LOGIN_STATE, user);

        // 设置登录时间
        User userUpdate = new User();
        userUpdate.setId(user.getId());
        userUpdate.setLastLoginTime(new Date());
        boolean result = this.updateById(userUpdate);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR,"登录失败");

        return this.getUserVO(user);
    }

    /**
     * 用户注册
     * @param userRegisterRequest 用户注册请求类
     * @return 是否成功
     */
    @Override
    public boolean userRegister(UserRegisterRequest userRegisterRequest) {
        String account = userRegisterRequest.getAccount();
        String password = userRegisterRequest.getPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        String email = userRegisterRequest.getEmail();
        Integer role = userRegisterRequest.getRole();
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(role);

        ThrowUtils.throwIf(userRoleEnum == null, ErrorCode.PARAMS_ERROR,"用户角色错误");
        ThrowUtils.throwIf(!password.equals(checkPassword), ErrorCode.PARAMS_ERROR,"两次密码不一致");

        // 校验账号是否已经存在
        long count = this.lambdaQuery().eq(User::getAccount, account).count();
        ThrowUtils.throwIf(count > 0, ErrorCode.PARAMS_ERROR,"账号已存在");

        // 对密码进行加密
        String encryptPassword = this.encryptPassword(password);

        // 构建用户实体类
        User user = new User();
        BeanUtil.copyProperties(userRegisterRequest,user);
        user.setPassword(encryptPassword);

        // 操作数据库
        boolean result = this.save(user);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"用户注册失败");
        }
        return true;
    }

    /**
     * 编辑用户信息
     * @param userEditRequest 用户编辑请求类
     * @return 是否成功
     */
    @Override
    public boolean userEdit(UserEditRequest userEditRequest) {
        Long id = userEditRequest.getId();
        String nick = userEditRequest.getNick();

        // 校验用户是否存在
        long count = this.lambdaQuery().eq(User::getId, id).count();
        ThrowUtils.throwIf(count == 0, ErrorCode.NOT_FOUND_ERROR,"用户不存在");

        // 构建用户实体类
        User user = new User();
        BeanUtil.copyProperties(userEditRequest,user);

        // 操作数据库
        boolean result = this.updateById(user);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"编辑用户信息失败");
        }
        return true;
    }


    /**
     * 删除用户
     * @param deleteRequest 删除用户请求类
     * @return 是否成功
     */
    @Override
    public boolean userDelete(DeleteRequest deleteRequest) {
        Long id = deleteRequest.getId();

        // 校验用户是否存在
        User user = this.getById(id);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR,"用户不存在");

        // 超级管理员不能被删除
        Integer role = user.getRole();
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(role);
        ThrowUtils.throwIf(userRoleEnum == UserRoleEnum.SUPER_ADMIN, ErrorCode.NO_AUTH_ERROR,"不能删除超级管理员");

        // 操作数据库
        boolean result = this.removeById(id);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"删除用户失败");

        }
        return true;
    }


    /**
     * 用户邮箱验证码登录
     * @param userEmailLoginRequest 邮箱验证码登录请求类
     * @param request
     * @return 用户信息
     */
    @Override
    public UserVO userEmailLogin(UserEmailLoginRequest userEmailLoginRequest,HttpServletRequest request) {
        String email = userEmailLoginRequest.getEmail();
        String code = userEmailLoginRequest.getCode();

        String key = RedisConstant.ACCOUNT_LOGIN_PREFIX + email;
        String oldCode =(String) redisTemplate.opsForValue().get(key);


        // 校验邮箱验证码是否存在
        ThrowUtils.throwIf(StrUtil.isBlank(oldCode), ErrorCode.PARAMS_ERROR,"验证码不存在");

        // 校验邮箱验证码是否正确
        ThrowUtils.throwIf(!code.equals(oldCode), ErrorCode.PARAMS_ERROR,"验证码错误");

        User user = this.lambdaQuery().eq(User::getEmail, email).one();

        // 保存用户的登录态 脱敏
        request.getSession().setAttribute(SessionConstant.USER_LOGIN_STATE, user);

        // 设置登录时间
        User userUpdate = new User();
        userUpdate.setId(user.getId());
        userUpdate.setLastLoginTime(new Date());
        boolean result = this.updateById(userUpdate);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR,"登录失败");

        return this.getUserVO(user);
    }


    /**
     * 获取当前登录用户
     * @param request
     * @return 用户信息
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
       User user = (User) request.getSession().getAttribute(SessionConstant.USER_LOGIN_STATE);
       ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR,"用户未登录");
       return user;
    }

    /**
     * 获取邮箱验证码
     * @param userGetEmailCodeRequest 获取邮箱验证码请求类
     * @return 是否成功
     */
    @Override
    public boolean userGetEmailCode(UserGetEmailCodeRequest userGetEmailCodeRequest) {
        String email = userGetEmailCodeRequest.getEmail();
        Integer sendEmailType = userGetEmailCodeRequest.getSendEmailType();
        SendEmailCodeEnum sendEmailCodeEnum = SendEmailCodeEnum.getEnumByValue(sendEmailType);

        ThrowUtils.throwIf(sendEmailCodeEnum == null, ErrorCode.PARAMS_ERROR,"发送邮箱验证码类型错误");

        // 校验邮箱是否已经存在
        long count = this.lambdaQuery().eq(User::getEmail, email).count();
        ThrowUtils.throwIf(count > 0 && sendEmailCodeEnum == SendEmailCodeEnum.Register, ErrorCode.PARAMS_ERROR,"邮箱已被使用");
        ThrowUtils.throwIf(count == 0 && sendEmailCodeEnum == SendEmailCodeEnum.Login, ErrorCode.PARAMS_ERROR,"邮箱不存在");

        // 校验验证码是否在有效期内
        String key;
        if(sendEmailCodeEnum == SendEmailCodeEnum.Login){
            key = RedisConstant.ACCOUNT_LOGIN_PREFIX + email;
        }else {
            key = RedisConstant.ACCOUNT_REGISTER_PREFIX + email;
        }
        String oldCode =(String) redisTemplate.opsForValue().get(key);
        ThrowUtils.throwIf(StrUtil.isNotBlank(oldCode), ErrorCode.PARAMS_ERROR, "验证码还在有效期内，请勿重复发送");

        // 生成邮箱验证码并存入redis中
        String emailCode =  RandomUtil.randomString(6);
        redisTemplate.opsForValue().set(key,emailCode,RedisConstant.EMAIL_CODE_EXPIRE_TIME, TimeUnit.MINUTES);

        // 发送邮箱验证码
        asyncService.sendEmailCode(email,emailCode,sendEmailCodeEnum);

        return true;
    }

    /**
     * 用户登出
     * @param request
     * @return 是否成功
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        Object userObj = request.getSession().getAttribute(SessionConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(userObj == null, ErrorCode.PARAMS_ERROR, "未登录");
        // 移除登录态
        request.getSession().removeAttribute(SessionConstant.USER_LOGIN_STATE);
        return true;
    }

    /**
     * 重置用户密码
     * @param userUpdatePasswordRequest 用户重置密码请求类
     * @return 是否成功
     */
    @Override
    public boolean updatePassword(UserUpdatePasswordRequest userUpdatePasswordRequest) {
        Long id = userUpdatePasswordRequest.getId();
        String oldPassword = userUpdatePasswordRequest.getOldPassword();
        String newPassword = userUpdatePasswordRequest.getNewPassword();
        String checkPassword = userUpdatePasswordRequest.getCheckPassword();

        User oldUser = this.getById(id);
        ThrowUtils.throwIf(oldUser == null, ErrorCode.PARAMS_ERROR, "用户不存在");

        ThrowUtils.throwIf(!oldUser.getPassword().equals(this.encryptPassword(oldPassword)), ErrorCode.PARAMS_ERROR, "旧密码错误");
        ThrowUtils.throwIf(oldPassword.equals(newPassword), ErrorCode.PARAMS_ERROR, "新密码不能与旧密码相同");
        ThrowUtils.throwIf(!newPassword.equals(checkPassword), ErrorCode.PARAMS_ERROR, "两次密码不一致");

        User user = new User();
        user.setId(id);
        user.setPassword(this.encryptPassword(newPassword));
        boolean result = this.updateById(user);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"重置密码失败");
        }
        return true;
    }

    /**
     * 获取用户分页信息
     * @param userQueryRequest 用户查询请求类
     * @return 用户分页信息
     */
    @Override
    public Page<UserVO> getUserPage(UserQueryRequest userQueryRequest) {
        int current = userQueryRequest.getCurrent();
        int pageSize = userQueryRequest.getPageSize();

        LambdaQueryWrapper<User> queryWrapper =  this.getQueryWrapper(userQueryRequest);
        Page<User> userPage = this.page(new Page<>(current, pageSize), queryWrapper);
        List<User> userList = userPage.getRecords();
        List<UserVO> userVOList = this.getUserVOList(userList);
        Page<UserVO> userVOPage = new Page<>(current,pageSize,userPage.getTotal());
        userVOPage.setRecords(userVOList);
        return userVOPage;
    }

    /**
     * 添加用户
     * @param userAddRequest 添加用户请求类
     * @return 是否成功
     */
    @Override
    public boolean userAdd(UserAddRequest userAddRequest) {
        String account = userAddRequest.getAccount();
        String password = userAddRequest.getPassword();
        String email = userAddRequest.getEmail();
        Integer role = userAddRequest.getRole();
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(role);

        ThrowUtils.throwIf(userRoleEnum == null, ErrorCode.PARAMS_ERROR, "用户角色错误");

        long count1 = this.lambdaQuery().eq(User::getAccount, account).count();
        ThrowUtils.throwIf(count1 > 0, ErrorCode.PARAMS_ERROR, "用户账号已存在");

        long count2 = this.lambdaQuery().eq(User::getEmail, email).count();
        ThrowUtils.throwIf(count2 > 0, ErrorCode.PARAMS_ERROR, "用户邮箱已存在");

        User user = new User();
        BeanUtil.copyProperties(userAddRequest, user);
        user.setPassword(this.encryptPassword(password));

        boolean result = this.save(user);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"添加用户失败");
        }
        return true;
    }

    /**
     * 重置用户密码
     * @param userResetPasswordRequest 用户重置密码请求类
     * @return 是否成功
     */
    @Override
    public boolean resetPassword(UserResetPasswordRequest userResetPasswordRequest) {
        Long id = userResetPasswordRequest.getId();

        long count = this.lambdaQuery().eq(User::getId, id).count();
        ThrowUtils.throwIf(count == 0, ErrorCode.PARAMS_ERROR, "用户不存在");

        User user = new User();
        user.setId(id);
        user.setPassword(this.encryptPassword(INITIAL_PASSWORD));

        boolean result = this.updateById(user);
        if(!result){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"重置密码失败");
        }
        return true;
    }

    /**
     * 获取用户VO列表
     * @param userList 用户列表
     * @return 用户VO列表
     */
    @Override
    public List<UserVO> getUserVOList(List<User> userList) {
        if(CollUtil.isEmpty(userList)){
            return Collections.emptyList();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }


    /**
     * 获取用户查询条件
     * @param userQueryRequest 用户查询请求类
     * @return 用户查询条件
     */
    @Override
    public LambdaQueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        String account = userQueryRequest.getAccount();
        String nick = userQueryRequest.getNick();
        String email = userQueryRequest.getEmail();
        Integer role = userQueryRequest.getRole();

        return Wrappers.lambdaQuery(User.class)
                .eq(StrUtil.isNotBlank(account), User::getAccount, account)
                .eq(StrUtil.isNotBlank(email), User::getEmail, email)
                .eq(ObjUtil.isNotNull(role), User::getRole, role)
                .like(StrUtil.isNotBlank(nick), User::getNick, nick)
                .orderByDesc(User::getRole,User::getCreateTime);
    }


    /**
     * 获取用户VO
     * @param user 用户
     * @return 用户VO
     */
    @Override
    public UserVO getUserVO(User user){
        ThrowUtils.throwIf(user == null,ErrorCode.PARAMS_ERROR);
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(user, userVO);
        return userVO;
    }


    /**
     * 获取加密后的密码
     * @param password 密码
     * @return 加密之后的密码
     */
    @Override
    public String encryptPassword(String password){
        // 加盐，混淆密码
        return DigestUtils.md5DigestAsHex((SALT + password).getBytes());
    }




}




